[Swift] Swift 3.0.1 の変更点
Xcode 8.1 がリリース
Xcode 8.1 が正式リリースされました。iOS は iOS 10.1 向けの、macOS は macOS Sierra 10.12.1 向けのアプリケーションを作ることができます。特に macOS については MacBook Pro に搭載される Touch Bar が操れる NSTouchBar が SDK に追加されていたりで、何かと話題でしたね。
Xcode 8.1 には Swift のパッチバージョンとも言える Swift 3.0.1 が同梱されています。つまり Xcode 8.1 で使うことのできる Swift のデフォルトバージョンは 3.0.1 ということになります。
Swift 3.0 からどのような変更点があるか、リリースノートを参考にざっくり見ておきたいと思います。
新機能
nil を Objective-C の nonnull の id にブリッジするときに NSNull としてブリッジする
例えば次のような Objective-C のクラスがあり、
#import <Foundation/Foundation.h> @interface SampleClass : NSObject - (void)imported: (id _Nonnull)value; @end
#import "SampleClass.h" @implementation SampleClass - (void)imported:(id)value { NSLog(@"%@", value); } @end
Swift 側ではこんな感じで呼んでみることとします。
let a: String? = "hello", b: String? = nil SampleClass().imported(a) SampleClass().imported(b)
このとき、 Swift 3.0 では _SwiftValue
として渡されていました。
_SwiftValue
は、Objective-C に対応する型がない場合に使われる型です。Optional が _SwiftValue
として渡されてしまうと、値が空なのかどうなのかが解りづらく、バグを生みやすいという問題がありました。
そこで、Swift 3.0.1 では NSNull.null にブリッジされるようになりました。これで、値が空の場合は明確に判別できるようになりました。
Swift の数値型を Objective-C にブリッジするときに NSNumber としてブリッジする
これは SE-0140 に近い話です。Swift では数値の型は Int8 や UInt8、 Int16、 UInt16… と言ったように、ビット幅に合わせた型が用意されています。また、Float や Double と言った浮動小数点型もあります。
これらを Objective-C にブリッジする場合、Swift 3.0 では _SwiftValue
として渡されていました。Swift 3.0.1 からは、標準的な数値の型はすべて NSNumber にブリッジされるようになりました。
また、CGRect や CGFloat なども同様の変更がありました。Swift 3.0 では _SwiftValue
として渡されていましたが、Swift 3.0.1 からは NSValue にブリッジされるようになりました。
UnsafeRawBufferPointer と UnsafeMutableRawBufferPointer の追加
UnsafeRawBufferPointer と UnsafeMutableRawBufferPointer が、新しい型として Swift Standard Library に追加されました。
これらはC言語互換の、ポインタを扱うための型のひとつです。Swift 3.0 では UnsafeRawPointer が導入されましたが、これのバッファ版、つまり固定的なメモリ領域(バッファ)を確保することのできる型です。既に UnsafeBufferPointer がありますが、これの UnsafeBufferPointer
例えば、Int の配列 (Array) から UnsafeMutableRawBufferPointer の配列を作るには、次のように書きます。
let intArray = [1, 2, 3] var byteBuffer = [UInt8]() intArray.withUnsafeBytes { (bytes: UnsafeRawBufferPointer) in byteBuffer += bytes } // [1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0] になる byteBuffer
Pointer 系から RowPointer 系へ移行するガイドは、以下で公開されています。より詳しく知りたい方は参考にしてください。
__swift__
マクロの追加
__swift__
マクロを使って、Swift のバージョンを Objective-C 側で判別できるようになりました。例えば、以下のように判定します。
#if !defined(__swift__) || __swift__ >= 30001 NSLog(@"Swift 3.0.1 以上だよ"); #else NSLog(@"Swift 3.0.1 未満だよ"); #endif
上記コードを Xcode 8.1 で実行すると「Swift 3.0.1 以上だよ」がコンソールに出力されます。
5桁の整数で表現されています(XXYZZ
)。現時点では Swift 3.0.1 以上かどうかしか判別できませんが、今後は Swift のバージョンアップに応じて、Objective-C 側で条件分岐させることができます。今回のアップデートのように、型のブリッジがちょっと変わったりすることがあるので、有用そうです。
バグ修正
@NSManaged
プロパティをプロトコルで使用するときの問題の解消- Objective-C のプロパティ使用時、 Xcode 8 で コンパイルした LTO ファイル (Link Time Optimization) と 旧 Xcode でコンパイルした LTO ファイルをリンクできない問題の解消
swift_error(zero_result)
がハンドリングできない問題の解消- プロトコルに定義したメンバーをオーバーライドしたときのアクセスチェック時にクラッシュする問題を解消 (Xcode 8.0 と互換性を保つ場合はクラスやストラクチャの定義のトップレベルに
private
を使う必要あり) UnsafePointer.withMemoryRebound(to:capacity:)
を通して得られる値の型がUnsafePointer
ではなくUnsafeMutablePointer
になっていた問題を解消 (つまり Swift 3.0.1 ではUnsafePointer
が得られる)
まとめ
Objective-C とのブリッジの話が多かったです。Objective-C をまだ捨てていない、というよりは Swift に移行しやすいように環境を整えている印象を受けました。